﻿using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using RuoVea.ExConfig;
using RuoVea.ExUtil;
using RuoVea.Identity.Utils;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;

namespace RuoVea.Identity.Client;

public static class AuthorizeExtion
{
    /// <summary>
    /// DI
    /// </summary>
    /// <param name="services"></param>
    public static void AddIdentityClient(this IServiceCollection services)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
        services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.Secure = CookieSecurePolicy.SameAsRequest; });
        ClientConfig.authClientInfo = Appsettings.GetConfig<AuthClientInfo>("AuthClientInfo");
    }
    /// <summary>
    /// DI 
    /// </summary>
    /// <param name="services"></param>
    /// <param name="authClientInfo"> 客户端配置名称 </param>
    public static void AddIdentityClient(this IServiceCollection services, string authClientInfo)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
        services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.Secure = CookieSecurePolicy.SameAsRequest; });
        ClientConfig.authClientInfo = Appsettings.GetConfig<AuthClientInfo>("AuthClientInfo");
    }
    /// <summary>
    /// DI配置
    /// </summary>
    /// <param name="services"></param>
    /// <param name="authClientInfo"></param>
    public static void AddIdentityClient(this IServiceCollection services, AuthClientInfo authClientInfo)
    {
        services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme);
        services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.Secure = CookieSecurePolicy.SameAsRequest; });
        ClientConfig.authClientInfo = authClientInfo;
    }
    /// <summary>
    /// 客户端引用
    /// </summary>
    /// <param name="app"></param>
    /// <returns></returns>
    public static WebApplication AddClientEndpoint(this WebApplication app)
    {
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/sine-olid", async context =>{ });

            endpoints.MapGet("/sine-out", async context => {

                AuthClientInfo authClientInfo = ClientConfig.authClientInfo;
                
                context.Response.Cookies.Delete("Cookies");
                context.Response.Cookies.Delete(".AspNetCore.Cookies");
                context.Response.Cookies.Delete("idsrv");
                context.Response.Cookies.Delete("idsrv.session");
                context.Response.Cookies.Delete("Hm_lpvt_58e98965a39ff5dab9a778a4a30eca6b");
                context.Response.Cookies.Delete("Hm_lvt_58e98965a39ff5dab9a778a4a30eca6b");
                context.Response.Cookies.Delete("token");
                await context.SignOutAsync();
                string uri = authClientInfo.ServerUrl + "/auth/logout?logoutId=1";
                context.Response.Redirect(uri);
            });

            endpoints.MapGet("/callback", async context =>
            {
                var returnurl = context?.Request?.Query?.SingleOrDefault(x => x.Key == "returnurl").Value.ToString();
                AuthClientInfo authClientInfo = ClientConfig.authClientInfo;

                var iIdentity = context?.User?.Identity;
                if (iIdentity == null || iIdentity.IsAuthenticated)
                {
                    if (returnurl.IsNullOrWhiteSpace())
                        context.Response.Redirect("/sine-olid");
                    else
                        context.Response.Redirect(returnurl);
                }
                else
                {
                    if (context != null)
                    {
                        var state = context.Request.Query.SingleOrDefault(x => x.Key == "state").Value.ToString();
                        var nonce = context.Request.Query.SingleOrDefault(x => x.Key == "nonce").Value.ToString();

                        var id_token = context.Request.Query.SingleOrDefault(x => x.Key == "id_token").Value.ToString();
                        var token = context.Request.Query.SingleOrDefault(x => x.Key == "token").Value.ToString();
                        if (id_token == "err" || token.IsNullOrWhiteSpace()) { await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("Request invalid")); return; }
                        var access_token = context.Request.Query.SingleOrDefault(x => x.Key == "access_token").Value.ToString();
                        var expires_in = context.Request.Query.SingleOrDefault(x => x.Key == "expires_in").Value.ToString()?.ToDoubleOrNull() ?? 1440;

                            //var token_type = context.Request.Query.SingleOrDefault(x => x.Key == "token_type").Value.ToString();
                            //var scope = context.Request.Query.SingleOrDefault(x => x.Key == "scope").Value.ToString();
                            //var session_state = context.Request.Query.SingleOrDefault(x => x.Key == "session_state").Value.ToString();
                        if (authClientInfo.state != state || state.IsNullOrWhiteSpace() || nonce.IsNullOrWhiteSpace())
                        {
                            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("非法请求"));
                            return;
                        }
                        if (id_token.IsNullOrWhiteSpace())
                        {
                            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("id_token invalid"));
                            return;
                        }
                        if (access_token.IsNullOrWhiteSpace())
                        {
                            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("access_token invalid"));
                            return;
                        }

                        string enrNonce = ConfigUtil.EnrNonce(authClientInfo.ClientId, authClientInfo.ClientSecrets, authClientInfo.HostUri + authClientInfo.RedirectUris, authClientInfo.ResponseType.ToString());

                        if (enrNonce != nonce)
                        {
                            await context.Response.Body.WriteAsync(Encoding.UTF8.GetBytes("非法请求"));
                            return;
                        }

                        var jwtHandler = new JwtSecurityTokenHandler();
                        JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(access_token);

                        var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
                        identity.AddClaims(jwtToken.Claims);
                        ClaimsPrincipal claims = new ClaimsPrincipal(identity) { };
                        await context.SignInAsync(identity.AuthenticationType, claims, new AuthenticationProperties { ExpiresUtc = new System.DateTimeOffset(dateTime: DateTime.Now.AddMinutes(expires_in)) });
                        context.Response.Cookies.Append("Cookies", access_token);

                        if (returnurl.IsNullOrWhiteSpace())
                            context.Response.Redirect(authClientInfo.HostUri + "/sine-olid");
                        else
                            context.Response.Redirect(returnurl);
                    }
                }
            });
        });
        return app;
    }
}
